import { Plugin } from "../editor"
import { Operation, SetSelectionOperation } from "../operations"
import { debounce } from "../utils"

let undoPaths: Operation[][] = []
let redoPaths: Operation[][] = []
let batchOps: Operation[] = []

// 对需要合并的op进行合并
const addHistory = debounce((operations: Operation[]) => {
  // 比如连续的光标变化op、连续的输入、连续的删除等可以合并
  // 存储在undos数组中
  if (
    operations.every((operation) => operation instanceof SetSelectionOperation)
  )
    return

  // 删除尾部多余的光标操作
  let lastOp: Operation | null = null
  while (operations.length) {
    const cur = operations.pop()!

    if (!(cur instanceof SetSelectionOperation)) {
      operations.push(cur)
      lastOp && operations.push(lastOp)
      break
    }

    lastOp = cur
  }

  // 删除头部多余的光标操作
  while (operations.length) {
    const firstOp = operations.shift()!
    if (!(firstOp instanceof SetSelectionOperation)) {
      operations.unshift(firstOp)
      break
    }
  }

  operations.length && undoPaths.push([...operations].reverse())
  batchOps = []
}, 300)

export const undoRedoPlugin: Plugin = (editor, operation, originalApply) => {
  batchOps.push(operation)
  addHistory(batchOps)

  editor.undos = function () {
    const batch = undoPaths.pop()
    if (batch) {
      batch
        .map((operation) => {
          return operation?.reverse?.(this)
        })
        .flat(2)
        .forEach((op) => {
          originalApply(op)
        })
      redoPaths.push(batch.reverse())
    }
  }

  editor.redo = function () {
    const batch = redoPaths.pop()
    if (batch) {
      batch.forEach((op) => {
        originalApply(op)
      })

      undoPaths.push(batch.reverse())
    }
  }

  return () => {
    undoPaths = []
    redoPaths = []
    batchOps = []
  }
}
